/*
 * Copyright (C) 2012 Lightbox
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package iamutkarshtiwari.github.io.ananas.editimage.utils;

import ohos.aafwk.ability.DataAbilityHelper;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.data.rdb.ValuesBucket;
import ohos.media.image.ImagePacker;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.ImageInfo;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;
import ohos.media.photokit.metadata.AVStorage;
import ohos.utils.net.Uri;

import java.io.*;
import java.nio.ByteBuffer;
import java.util.Optional;

public class BitmapUtils {
    /**
     * Used to tag logs
     */
    @SuppressWarnings("unused")
    private static final String TAG = "BitmapUtils";

    public static final long MAX_SZIE = 1024 * 512;// 500KB

    public static Size getBitmapSize(String filePath) { //BitmapSize
        PixelMap pixelMap = getPixMapFromPath(filePath);
        if (pixelMap == null) return null;
        ImageInfo info = pixelMap.getImageInfo();
        return info.size;
    }

    /**
     * 从图片路径获取PixelMap
     *
     * @param filePath 图片路径
     * @return 根据图片路径获取的PixelMap
     */
    public static PixelMap getPixMapFromPath(String filePath) {
        ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
        sourceOptions.formatHint = "image/jpeg";
        ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();
        decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;

        FileInputStream fileInputStream = null;
        try {
            File myFile = new File(filePath);
            fileInputStream = new FileInputStream(myFile);
            ImageSource imageSource = ImageSource.create(fileInputStream, sourceOptions);
            return Optional.ofNullable(imageSource.createPixelmap(decodingOptions)).get();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 从Uri获取PixelMap
     *
     * @param uri     图片的uri
     * @param context Context上下文
     * @return 从uri获取的图片的 PixelMap
     */
    public static PixelMap getPixMapFromUri(Uri uri, Context context) {
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        FileDescriptor filedesc = null;
        try {
            filedesc = helper.openFile(uri, "r");
            ImageSource imageSource = ImageSource.create(filedesc, null);
            ImageSource.DecodingOptions decodingOpts = new ImageSource.DecodingOptions();
            return imageSource.createThumbnailPixelmap(decodingOpts, true);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            helper.release();
        }

        return null;
    }

    public static BitmapSize getScaledSize(int originalWidth, int originalHeight, int numPixels) {
        float ratio = (float) originalWidth / originalHeight;

        int scaledHeight = (int) Math.sqrt((float) numPixels / ratio);
        int scaledWidth = (int) (ratio * Math.sqrt((float) numPixels
                / ratio));

        return new BitmapSize(scaledWidth, scaledHeight);
    }

    public static class BitmapSize {
        public int width;
        public int height;

        public BitmapSize(int width, int height) {
            this.width = width;
            this.height = height;
        }
    }

    public static byte[] bitmapTobytes(PixelMap bitmap) {
        return BitmapUtils.bitmapTobytesCompress(bitmap, 30);
    }

    public static byte[] bitmapTobytesNoCompress(PixelMap bitmap) {
        return BitmapUtils.bitmapTobytesCompress(bitmap, 100);
    }

    public static byte[] bitmapTobytesCompress(PixelMap bitmap, int quality) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ImagePacker.PackingOptions opt = new ImagePacker.PackingOptions();
        opt.format = "image/png";
        opt.quality = quality;
        ImagePacker packer = ImagePacker.create();
//        boolean result = packer.initializePacking(outputStream, opt);
//        result = packer.addImage(bitmap);
//        long dataSize = packer.finalizePacking();
        byte[] data = outputStream.toByteArray();
        packer.release();

        return data;
    }


    public static PixelMap genRotateBitmap(byte[] data) {
//        Bitmap bMap = BitmapFactory.decodeByteArray(data, 0, data.length);
//        Matrix matrix = new Matrix();
//        matrix.reset();
//        matrix.postRotate(90);
//        Bitmap bMapRotate = Bitmap.createBitmap(bMap, 0, 0, bMap.getWidth(),
//                bMap.getHeight(), matrix, true);
//        bMap.recycle();

        ImageSource image = BitmapUtils.byteToIamge(data);
        PixelMap bMap = BitmapUtils.rotateImage(image, 90);

        ImageInfo info = bMap.getImageInfo();
        PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
        options.size = info.size;
        options.pixelFormat = info.pixelFormat;
        PixelMap bMapRotate = PixelMap.create(bMap, options);

        bMap.release();
        bMap = null;
        image.release();
//        System.gc();
        return bMapRotate;
    }

    public static PixelMap byteToBitmap(byte[] data) {
//        return BitmapFactory.decodeByteArray(data, 0, data.length);
        ImageSource.SourceOptions opt = new ImageSource.SourceOptions();
        ImageSource image = ImageSource.create(data, 0, data.length, opt);
        ImageSource.DecodingOptions deOpt = new ImageSource.DecodingOptions();
        return image.createPixelmap(deOpt);
    }

    public static ImageSource byteToIamge(byte[] data) {
        ImageSource.SourceOptions opt = new ImageSource.SourceOptions();
        ImageSource image = ImageSource.create(data, 0, data.length, opt);

        return image;
    }

    public static PixelMap rotateImage(ImageSource image, float angel) {
        ImageSource.DecodingOptions deOpt = new ImageSource.DecodingOptions();
        deOpt.rotateDegrees = angel;

        return image.createPixelmap(deOpt);
    }

    /**
     * Resize a bitmap object to fit the passed width and height and rotation
     *
     * @param input      The bitmap to be resized
     * @param destWidth  Desired maximum width of the result bitmap
     * @param destHeight Desired maximum height of the result bitmap
     * @param rotation   Rotation angle
     * @return A new resized bitmap
     * @throws OutOfMemoryError if the operation exceeds the available vm memory
     */
    public static PixelMap resizeBitmap(final PixelMap input, int destWidth, int destHeight, int rotation) throws OutOfMemoryError {

        int dstWidth = destWidth;
        int dstHeight = destHeight;
        ImageInfo info = input.getImageInfo();
        final int srcWidth = info.size.width;// input.getWidth();
        final int srcHeight = info.size.height;// input.getHeight();

        if (rotation == 90 || rotation == 270) {
            dstWidth = destHeight;
            dstHeight = destWidth;
        }

        boolean needsResize = false;
        float pro;
        if ((srcWidth > dstWidth) || (srcHeight > dstHeight)) {
            needsResize = true;
            if ((srcWidth > srcHeight) && (srcWidth > dstWidth)) {
                pro = (float) dstWidth / (float) srcWidth;
                dstHeight = (int) (srcHeight * pro);
            } else {
                pro = (float) dstHeight / (float) srcHeight;
                dstWidth = (int) (srcWidth * pro);
            }
        } else {
            dstWidth = srcWidth;
            dstHeight = srcHeight;
        }

        if (needsResize || rotation != 0) {
            PixelMap output;

            if (rotation == 0) {
//                output = Bitmap.createScaledBitmap(input, dstWidth, dstHeight, true);
                PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
                opts.size = new Size(dstWidth, dstHeight);
                output = PixelMap.create(input, opts);
            } else {
                Matrix matrix = new Matrix();
                matrix.postScale((float) dstWidth / srcWidth, (float) dstHeight / srcHeight);
                matrix.postRotate(rotation);

                RectFloat srcRect = new RectFloat(0, 0, srcWidth, srcHeight);
                matrix.mapRect(srcRect);
                Rect dstRect = new Rect(0, 0, (int) srcRect.getWidth(), (int) srcRect.getHeight());

//                output = Bitmap.createBitmap(input, 0, 0, srcWidth, srcHeight, matrix, true);
                PixelMap.InitializationOptions opts = new PixelMap.InitializationOptions();
                opts.size = new Size(dstWidth, dstHeight);
                output = PixelMap.create(input, dstRect, opts);
            }
            return output;
        } else
            return input;
    }

    /**
     * Resize a bitmap object to fit the passed width and height
     *
     * @param input      The bitmap to be resized
     * @param destWidth  Desired maximum width of the result bitmap
     * @param destHeight Desired maximum height of the result bitmap
     * @return A new resized bitmap
     * @throws OutOfMemoryError if the operation exceeds the available vm memory
     */
    public static PixelMap resizeBitmap(final PixelMap input, int destWidth, int destHeight) throws OutOfMemoryError {
        return resizeBitmap(input, destWidth, destHeight, 0);
    }


    public static PixelMap rotateImage(PixelMap source, float angle) {
        ImageSource image = BitmapUtils.pixelMapToIamgeSource(source);
        PixelMap bitmap = null;
        bitmap = BitmapUtils.rotateImage(image, angle);
        image.release();

        return bitmap;
    }

    public static ImageSource pixelMapToIamgeSource(PixelMap source) {
        ByteBuffer buffer = null;
        source.readPixels(buffer);
        ImageSource.SourceOptions opt = new ImageSource.SourceOptions();
        return ImageSource.create(buffer, opt);
    }


    //    public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
    public static int calculateInSampleSize(ImageInfo options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        final int height = options.size.height;
        final int width = options.size.width;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) >= reqHeight
                    && (halfWidth / inSampleSize) >= reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    public static boolean saveBitmap(PixelMap bm, String filePath) {
        return BitmapUtils.saveBitmap(bm, filePath, 90);
    }

    /**
     * 通过真实路径保存图片
     *
     * @param bm       要保存的图片
     * @param filePath 路径
     * @param quality  压缩率
     * @return 是否成功
     */
    public static boolean saveBitmap(PixelMap bm, String filePath, int quality) {
        File file = new File(filePath);
        if (file.exists()) {
            if (file.delete()) {
                LogUtil.loge("file delete success");
            }
        }

        FileOutputStream out = null;
        ImagePacker packer = null;
        try {
//            bm.bmcompress(Bitmap.CompressFormat.PNG, 90, out);
            out = new FileOutputStream(file);
            ImagePacker.PackingOptions opt = new ImagePacker.PackingOptions();
            opt.format = "image/jpeg";
            opt.quality = quality;
            packer = ImagePacker.create();
            boolean result = packer.initializePacking(out, opt);
            result = packer.addImage(bm);
            long dataSize = packer.finalizePacking();
            return dataSize > 0;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        } finally {
            try {
                if (packer != null) {
                    packer.release();
                }
                if (out != null) {
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 通过在媒体数据库中插入uri保存图片
     *
     * @param bm       要保存的图片
     * @param fileName 文件名
     * @param quality  压缩率
     * @param context  Context
     * @return 是否成功
     */
    public static boolean saveBitmap(PixelMap bm, String fileName, int quality, Context context) {
        DataAbilityHelper helper = DataAbilityHelper.creator(context);
        ValuesBucket valuesBucket = null;
        OutputStream outputStream = null;

        try {
            valuesBucket = new ValuesBucket();
            valuesBucket.putString(AVStorage.Images.Media.DISPLAY_NAME, fileName);
            valuesBucket.putString("relative_path", "DCIM" + File.separator);
            valuesBucket.putString(AVStorage.Images.Media.MIME_TYPE, "image/JPEG");
            //应用独占
            valuesBucket.putInteger("is_pending", 1);

            int id = helper.insert(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, valuesBucket);
            Uri uri = Uri.appendEncodedPathToUri(AVStorage.Images.Media.EXTERNAL_DATA_ABILITY_URI, String.valueOf(id));
            //这里需要"w"写权限
            FileDescriptor fd = helper.openFile(uri, "w");
            outputStream = new FileOutputStream(fd);

            ImagePacker imagePacker = ImagePacker.create();
            ImagePacker.PackingOptions packingOptions = new ImagePacker.PackingOptions();
            packingOptions.format = "image/jpeg";
            packingOptions.quality = quality;

            long dataSize = -1;
            boolean result = imagePacker.initializePacking(outputStream, packingOptions);
            if (result) {
                result = imagePacker.addImage(bm);
                if (result) {
                    dataSize = imagePacker.finalizePacking();
                }
            }
            helper.update(uri, valuesBucket, null);

            return dataSize > 0;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.flush();
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (valuesBucket != null) {
                valuesBucket.clear();
                valuesBucket.putInteger("is_pending", 0);//解除独占
            }
            helper.release();
        }
        return false;
    }
}
